﻿using LightCAD.Core;
using LightCAD.Core.Elements;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace LightCAD.Runtime
{


    public class SelectedElement
    {
        public LcElement Element { get; set; }
        /// <summary>
        ///如果开启了用户坐标系，或者BlockRef/XrefRef等元素坐标系里的元素
        ///在位编辑状态，元素坐标系里的元素是可选，可编辑的
        /// </summary>
        public Matrix3d? Matrix { get; set; }

        public bool HasGrip { get; set; }
    }
    public class DocumentAction
    {
        private DocumentRuntime docRt;

        public event EventHandler SelectsChanged;

        public Box2d PastingBox { get; set; }
        public List<LcElement> PastingElements { get; set; } = new List<LcElement>();
        public LcElement HoveredElement { get; set; }
        public List<LcElement> SelectedElements { get; } = new List<LcElement>();
        public double DragSelectionX { get; set; }
        public double DragSelectionY { get; set; }

        public DocumentAction(DocumentRuntime docRt)
        {
            this.docRt = docRt;
            CommandCenter.Instance.CommandExecute += Instance_CommandExecute;
        }

        private void Instance_CommandExecute(object sender, CommandExecuteEventArgs e)
        {
            switch (e.Command.Name)
            {
                case "COPY": Copy(); e.Result.Success = true; break;
                case "CUT": Cut(); e.Result.Success = true; break;
                case "PASTE": Paste(); e.Result.Success = true; break;
                case "UNDO": Undo(); e.Result.Success = true; break;
                case "REDO": Redo(); e.Result.Success = true; break;
                case "ERASE": this.docRt.Erase(); e.Result.Success = true; break;

            }
        }


        #region Select

        public void SelectElements(List<LcElement> elements)
        {
            foreach (var ele in elements)
            {
                ele.IsSelected = true;
                SelectedElements.Add(ele);
            }
        }
        private ViewportRuntime VportRt { get { return this.docRt.DrawingEditRt.ActiveViewportRt; } }
        public List<LcElement> RectSelects(ElementSet container, Box2d selectBox, bool isInclude)
        {

            //TODO:RectSelects放在这里似乎不不合理

            //屏幕矩形，转换为世界坐标系多边形
            var selectPoly = selectBox.ToPolygon(VportRt.ScrToWcs);
            //if (!this.docRt.DrawingEditRt.ActiveViewportRt.Viewport.Ucs.IsIdentity())
            //{
            //    selectPoly = selectPoly.Multiply(this.docRt.DrawingEditRt.ActiveViewportRt.WcsToUcs);
            //}
            var elements = new List<LcElement>();
            int newSelected = 0;
            for (var i = container.Elements.Count - 1; i >= 0; i--)
            {
                var ele = container.Elements[i];
                ////如果处在在位编辑状态，处于顶层的Ref元素不可选择
                if (VportRt.IsRefEditing && ele == VportRt.RefEditingObject.RefLinks[0])
                {
                    var subSelects = new List<RefChildElement>();
                    var refSeleccted = isInclude ? ele.IncludedByBox(selectPoly, subSelects) : ele.IntersectWithBox(selectPoly, subSelects);
                    if (subSelects.Count > 0)
                    {
                        foreach (var subItem in subSelects)
                        {
                            //选中的子元素要属于指定的引用
                            if (subItem.RefObject == VportRt.RefEditingObject.Target)
                            {
                                if (!subItem.Element.IsSelected)
                                {
                                    // subItem.Element..CreateProxy(VportRt.RefEditingObject.TargetMatrix);
                                    subItem.Element.IsSelected = true;
                                    elements.Add(subItem.Element);
                                    SelectedElements.Add(subItem.Element);
                                }
                                else
                                {
                                    elements.Add(subItem.Element);
                                }
                            }
                        }
                    }
                    continue;
                }


                var selected = isInclude ? ele.IncludedByBox(selectPoly) : ele.IntersectWithBox(selectPoly);
                if (selected)
                {
                    if (!ele.IsSelected)
                    {
                        ele.IsSelected = true;
                        elements.Add(ele);
                        SelectedElements.Add(ele);
                        newSelected++;
                    }
                    else
                    {
                        elements.Add(ele);

                    }

                }
            }
            if (newSelected > 0)
            {
                SelectsChanged?.Invoke(this, EventArgs.Empty);
            }
            return elements;
        }
        public LcElement HitUnselect(ElementSet container, Vector2d hitPoint, double selectSize)
        {
            for (var i = container.Elements.Count - 1; i >= 0; i--)
            {
                var ele = container.Elements[i];
                var testBox = new Box2d(new Vector2d(hitPoint.X - selectSize, hitPoint.Y - selectSize), selectSize * 2, selectSize * 2);
                var intersected = ele.IntersectWithBox(testBox.ToPolygon());
                if (intersected)
                {
                    if (ele.IsSelected)
                    {
                        SelectedElements.Remove(ele);
                        SelectsChanged?.Invoke(this, EventArgs.Empty);

                        ele.IsSelected = false;
                        return ele;
                    }
                    return null;
                }
            }
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="container"></param>
        /// <param name="hitPoint"></param>
        /// <param name="selectSize"></param>
        /// <returns>选中的元素，是否是新选中</returns>
        public Tuple<LcElement, bool> HitSelect(ElementSet container, Vector2d hitPoint, double selectSize)
        {
            var testPoly = new Box2d(new Vector2d(hitPoint.X - selectSize, hitPoint.Y - selectSize), selectSize * 2, selectSize * 2).ToPolygon();
            for (var i = container.Elements.Count - 1; i >= 0; i--)
            {
                var ele = container.Elements[i];
                var intersected = ele.IntersectWithBox(testPoly);
                if (intersected)
                {
                    if (ele.IsSelected)
                    {
                        return new Tuple<LcElement, bool>(ele, false);
                    }
                    else
                    {
                        ele.IsSelected = true;
                        SelectedElements.Add(ele);
                        SelectsChanged?.Invoke(this, EventArgs.Empty);
                        return new Tuple<LcElement, bool>(ele, true);
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 设置选中元素的Drag状态
        /// </summary>
        /// <param name="dragging"></param>
        public void SetSelectsToDragging(bool dragging)
        {
            foreach (var ele in SelectedElements)
            {
                ele.RtStatus = dragging ? ElementStatus.Dragging : ElementStatus.Normal;
            }
        }
        /// <summary>
        /// 清除选中状态
        /// </summary>
        public void ClearSelects()
        {
            int selectedCount = SelectedElements.Count;
            foreach (var ele in SelectedElements)
            {
                ele.IsSelected = false;
            }
            SelectedElements.Clear();
            if (selectedCount > 0)
            {
                SelectsChanged?.Invoke(this, EventArgs.Empty);
            }
            this.ClearElementGrips();

        }

        #endregion


        #region Hover

        public LcElement HoverSelectElement(ElementSet container, Vector2d hitPoint, double selectSize)
        {
            if (HoveredElement != null)
            {
                HoveredElement.SetStatus(ElementStatus.Hovered, false);
                HoveredElement = null;
            }
            for (var i = container.Elements.Count - 1; i >= 0; i--)
            {
                var ele = container.Elements[i];
                var testBox = new Box2d(new Vector2d(hitPoint.X - selectSize, hitPoint.Y - selectSize), selectSize * 2, selectSize * 2);
                var intersected = ele.IntersectWithBox(testBox.ToPolygon());
                if (intersected)
                {
                    ele.SetStatus(ElementStatus.Hovered, true);
                    HoveredElement = ele;
                    return ele;
                }
            }
            return null;
        }

        public void HoverUnselectElement()
        {
            if (HoveredElement != null)
            {
                HoveredElement.SetStatus(ElementStatus.Hovered, false);
                HoveredElement = null;
            }
        }

        #endregion


        #region Drag
        internal void DragCopySelection(double dragX, double dragY)
        {
            DocumentManager.CurrentRecorder.BeginAction("DRAGCOPY");
            var copyElements = new List<LcElement>();
            foreach (var element in this.SelectedElements)
            {
                var copyEle = element.Clone();
                this.VportRt.ActiveElementSet.InsertElement(copyEle);
                copyElements.Add(copyEle);
            }
            foreach (var element in copyElements)
            {
                this.VportRt.ActiveElementSet.MoveElement(element, dragX, dragY);
            }
            this.SetSelectsToDragging(false);
            this.ClearSelects();
            this.SelectElements(copyElements);

            this.ResetElementGrips(copyElements);
            DocumentManager.CurrentRecorder.EndAction();

        }

        internal void DragSelection(double dragX, double dragY)
        {
            DocumentManager.CurrentRecorder.BeginAction("DRAGMOVE");
            foreach (var element in this.SelectedElements)
            {
                this.VportRt.ActiveElementSet.MoveElement(element, dragX, dragY);
            }
            this.ResetElementGrips(this.SelectedElements);
            DocumentManager.CurrentRecorder.EndAction();
        }

        #endregion

        #region Grip Control

        public List<ControlGrip> ElementGrips = new List<ControlGrip>();
        public ControlGrip SelectedGrip;
        public ControlGrip HoveredGrip;

        public void ResetElementGrips(List<LcElement> elements)
        {
            foreach (LcElement element in elements)
            {
                var grips = GetControlGrips(element);
                ElementGrips.AddRange(grips);
            }
        }

        public void ClearElementGrips(List<LcElement> elements)
        {
            foreach (LcElement element in elements)
            {
                for (var i = ElementGrips.Count - 1; i >= 0; i--)
                {
                    if (ElementGrips[i].Element == element)
                    {
                        ElementGrips.RemoveAt(i);
                    }
                }
            }
        }
        /// <summary>
        /// 取消控制点
        /// </summary>
        public void ClearElementGrips()
        {
            ElementGrips.Clear();
        }


        public ControlGrip HitSelectGrip(Vector2d hitPoint, double selectSize)
        {
            SelectedGrip = null;
            for (var i = ElementGrips.Count - 1; i >= 0; i--)
            {
                var grip = ElementGrips[i];
                var testBox = new Box2d(new Vector2d(hitPoint.X - selectSize, hitPoint.Y - selectSize), selectSize * 2, selectSize * 2);
                var selected = grip.IntersectWithBox(testBox);
                if (selected)
                {
                    grip.IsSelected = true;
                    SelectedGrip = grip;
                    return grip;
                }
            }
            return null;
        }
        public ControlGrip HoverSelectGrip(Vector2d hitPoint, double selectSize)
        {
            HoveredGrip = null;
            for (var i = ElementGrips.Count - 1; i >= 0; i--)
            {
                var grip = ElementGrips[i];
                var testBox = new Box2d(new Vector2d(hitPoint.X - selectSize, hitPoint.Y - selectSize), selectSize * 2, selectSize * 2);
                var hovered = grip.IntersectWithBox(testBox);
                if (hovered)
                {
                    grip.IsHovered = true;
                    HoveredGrip = grip;
                    return grip;
                }
            }
            return null;
        }



        public void HoverUnselectGrip()
        {
            if (HoveredGrip != null)
            {

                HoveredGrip.IsHovered = false;
                HoveredGrip = null;
            }
        }

        public Action<SKCanvas> DrawDraggingGrip;

        public void DraggingGrip(Vector2d pw)
        {
            var ele = SelectedGrip.Element;
            SetDragGrip(SelectedGrip.Name, pw, false);
        }
        public void DraggingGripEnd(Vector2d pw)
        {
            var ele = SelectedGrip.Element;
            (ele.RtAction as ElementAction).SetDragGrip(ele, SelectedGrip.Name, pw, true);
            ele.ResetBoundingBox();
            SelectsChanged?.Invoke(this, EventArgs.Empty);
            DrawDraggingGrip = null;
            ClearElementGrips(new List<LcElement> { ele });
            ResetElementGrips(new List<LcElement> { ele });
        }

        public ControlGrip[] GetControlGrips(LcElement element)
        {
            var action = element.RtAction as ElementAction;
            ControlGrip[] grips = action.SetViewport(this.docRt.DrawingEditRt.ActiveViewportRt).GetControlGrips(element);
            return grips;
        }

        public void SetDragGrip(string gripName, Vector2d position, bool isEnd)
        {
            var element = this.docRt.Action.SelectedGrip.Element;
            var actionHandler = (element.RtAction as ElementAction);
            actionHandler.SetDragGrip(element, gripName, position, isEnd);
        }

        #endregion

        #region Copy/Cust/Paste
        public void Copy()
        {
            if (this.SelectedElements.Count == 0) return;
            var elements = new List<LcElement>();
            var box = new Box2d();
            box.SetEmpty();
            foreach (var element in this.SelectedElements)
            {
                box.Union(element.BoundingBox);
                var copyEle = element.Clone();
                elements.Add(copyEle);
            }

            foreach (var element in elements)
            {
                element.Translate(-box.Location.X, -box.Location.Y);
            }

            var dataObject = AppRuntime.UISystem.NewClipboardDataObject();

            var stream = new MemoryStream();
            var soptions = new JsonSerializerOptions { WriteIndented = false };
            var options = new JsonWriterOptions { Indented = false };
            using (var writer = new Utf8JsonWriter(stream, options))
            {
                writer.WriteStartObject();
                writer.WritePropertyName("Elements");
                writer.WriteStartArray();
                foreach (var element in elements)
                {
                    writer.WriteElementObject(element, soptions);
                }
                writer.WriteEndArray();
                writer.WriteEndObject();
                writer.Flush();
            }
            var json = Encoding.UTF8.GetString(stream.ToArray());

            AppRuntime.UISystem.SetClipboardData(dataObject, "LightCAD_Elements", json);
            AppRuntime.UISystem.SetToClipboard(dataObject);
        }

        internal void Cut()
        {
            if (this.SelectedElements.Count == 0) return;
            Copy();
            DocumentManager.CurrentRecorder.BeginAction("CUT");
            foreach (var element in this.SelectedElements)
            {
                this.VportRt.ActiveElementSet.RemoveElement(element);
            }
            this.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }
        internal async void Paste()
        {
            CancelAll();
            var data = await AppRuntime.UISystem.GetClipboardData("LightCAD_Elements");
            if (data == null) return;
            var json = (data is String) ? (string)data : Encoding.Unicode.GetString((byte[])data).Trim('\0');
            var jdoc = JsonDocument.Parse(json);
            var jele = jdoc.RootElement;
            var elements = new List<LcElement>();
            var arr=jele.GetProperty("Elements").EnumerateArray().ToArray();
            for(var i=0;i<arr.Length; i++)
            {
               var ele= arr[i].ReadElementObject(this.docRt.Document);
                elements.Add(ele);
            }
            if (elements.Count == 0) return;
            foreach (var element in elements)
            {

                element.Initilize(this.docRt.Document);
                if (element is LcBlockRef)
                {
                    var blockRef = element as LcBlockRef;
                    var blk = this.docRt.Document.Blocks[blockRef.Block.Id];
                    blockRef.Block = blk;
                }
            }
            var box = new Box2d();
            box.SetEmpty();
            foreach (var element in elements)
            {
                box.Union(element.BoundingBox);
            }
            this.PastingBox = box;//用于显示粘贴内容的外框，暂时无用

            this.ClearSelects();
            this.PastingElements = elements;
            this.docRt.DrawingEditRt.ActiveViewportRt.SetStatus(ViewportStatus.Pasting);

        }
        internal void PasteDone()
        {
            var vportRt = this.docRt.DrawingEditRt.ActiveViewportRt;
            var pointerPos = vportRt.PointerPressedPosition.ToVector2d();
            var wp = vportRt.ConvertScrToWcs(pointerPos);
            DocumentManager.CurrentRecorder.BeginAction("PASTE");
            foreach (var element in this.PastingElements)
            {
                //element.Initilize(this.docRt.Document);
                //if (element is LcBlockRef)
                //{
                //    var blockRef = element as LcBlockRef;
                //    var blk = this.docRt.Document.Blocks[blockRef.BlockId];
                //    blockRef.Block = blk;
                //}

                //  var copyEle = element.Clone();
                //copyEle.Initilize(this.docRt.Document);//来自剪切板的元素没有Document上下文，需初始化
                element.Translate(wp.X, wp.Y);
                this.VportRt.ActiveElementSet.InsertElement(element);
            }
            DocumentManager.CurrentRecorder.EndAction();
        }

        #endregion


        #region Mics
        public void CancelAll()
        {
            this.docRt.DrawingEditRt.ActiveViewportRt.CancelAll();
        }
        internal void Undo()
        {
            CancelAll();
            DocumentManager.CurrentRecorder.UndoAction();
        }
        internal void Redo()
        {
            DocumentManager.CurrentRecorder.RedoAction();
        }
        #endregion
    }
}
